
local BlockInputField = import("./BlockInputField.lua");
local InputFieldContainer = import("./InputFieldContainer.lua");
local Connection = import("./Connection.lua");

local FieldSpace = import("./Field/Space.lua");
local FieldLabel = import("./Field/Label.lua");
local FieldInput = import("./Field/Input.lua");
local FieldSelect = import("./Field/Select.lua");
local InputValue = import("./Input/Value.lua");
local InputStatement = import("./Input/Statement.lua");

local Block = commonlib.inherit(BlockInputField);

Block:Property("Id");                                                          -- ID
Block:Property("Color");                                                       -- 颜色
Block:Property("Draggable", true, "IsDraggable");                              -- 是否可以拖拽
Block:Property("Dragging", true, "IsDragging");                                -- 是否在拖拽中
Block:Property("ToolBoxBlock", false, "IsToolBoxBlock");                       -- 是否是工具箱块
Block:Property("HideInToolBox", false, "IsHideInToolBox");                     -- 是否在工具栏中隐藏

local __NID__ = 1;
function Block:ctor()
    self:SetId(__NID__);
    __NID__ = __NID__ + 1;

    self.__input_field_container_list__ = {};           -- 输入字段容器列表
    self.__input_field_option_list__ = {};              -- 输入字段选项列表
    self.__input_field_map__ = {};

    self:SetToolBoxBlock(false);
    self:SetDraggable(true);
    self:SetDragging(false);
end

function Block:Init(blockly, opt)
    self:SetBlockly(blockly);

    Block._super.Init(self, self, opt);

    self:SetColor(self:ToColor(opt.color or 0x2E9BEF));
    self:SetDraggable(commonlib.if_else(opt.isDraggable == false, false, true));

    if (opt.id) then self:SetId(opt.id) end
    if (opt.output) then self.__output_connection__ = Connection:new():Init(self, "output_connection", opt.output) end
    if (opt.previousStatement) then self.__previous_connection__ = Connection:new():Init(self, "previous_connection", opt.previousStatement) end
    if (opt.nextStatement) then self.__next_connection__ = Connection:new():Init(self, "next_connection", opt.nextStatement) end
    
    self:ParseMessageAndArg(opt);

    return self;
end

function Block:ParseMessageAndArg(opt)
    local message = opt.message;
    local arg = opt.arg;

    local inputFieldContainer = InputFieldContainer:new():Init(self, true);
    table.insert(self.__input_field_container_list__, inputFieldContainer);

    local lastNo = 0;
    local startPos, len = 1, string.len(message);
    while(startPos <= len) do
        local pos = string.find(message, "%%", startPos);
        if (not pos) then pos = len + 1 end
        local nostr = string.match(message, "%%([%*%d]+)", startPos) or "";
        local no, nolen = tonumber(nostr), string.len(nostr);
        local text = string.sub(message, startPos, pos - 1) or "";
        local textlen = string.len(text);
        text = string.gsub(string.gsub(text, "^%s*", ""), "%s*$", "");
        -- 添加FieldLabel
        if (text ~= "") then inputFieldContainer:AddInputField(FieldLabel:new():Init(self, {value = text, type = "field_label"}), true) end
        no = no or (nolen > 0 and lastNo + 1 or nil);
        if (no and arg and arg[no]) then
            -- 添加InputAndField
            local inputFieldOpt = arg[no];
            local inputField = nil;
            if (inputFieldOpt.type == "field_space") then
                inputField = FieldSpace:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, false);
            elseif (inputFieldOpt.type == "field_input" or inputFieldOpt.type == "field_number") then
                inputField = FieldInput:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_color") then
                inputField = FieldColor:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_button") then
                inputField = FieldButton:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_value") then
                inputField = FieldValue:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_textarea") then
                inputField = FieldTextarea:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_json") then
                inputField = FieldJson:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_select" or inputFieldOpt.type == "field_dropdown") then
                inputField = FieldSelect:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "field_variable") then
                inputField = FieldVariable:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "input_dummy") then
            elseif (inputFieldOpt.type == "input_value") then
                inputField = InputValue:new():Init(self, inputFieldOpt); 
                inputFieldContainer:AddInputField(inputField, true);
            elseif (inputFieldOpt.type == "input_value_list") then
                inputField = InputValueList:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField, true);
            elseif (not self:IsOutput() and inputFieldOpt.type == "input_statement") then
                inputFieldContainer = InputFieldContainer:new():Init(self, false);
                table.insert(self.__input_field_container_list__, inputFieldContainer);
                inputField = InputStatement:new():Init(self, inputFieldOpt);
                inputFieldContainer:AddInputField(inputField);
                inputFieldContainer:SetInputStatementContainer(true);
                inputFieldContainer = InputFieldContainer:new():Init(self, true);
                table.insert(self.__input_field_container_list__, inputFieldContainer);
            end
            if (inputField) then
                table.insert(self.__input_field_option_list__, inputFieldOpt);
                if (inputFieldOpt.name) then self.__input_field_map__[inputFieldOpt.name] = inputField end 
            end
        end
        startPos = pos + 1 + nolen;
        lastNo = no or lastNo;
    end
    if (self:IsOutput()) then inputFieldContainer:TrimSpaceField() end 
end

function Block:GetInputField(input_field_name)
    return input_field_name and self.__input_field_map__[input_field_name];
end

function Block:IsOutput()
    return self.__output_connection__ ~= nil;
end

function Block:IsStatement()
    return self.__previous_connection__ ~= nil and self.__previous_connection__ ~= nil;
end

function Block:IsStart()
    return self.__previous_connection__ == nil and self.__next_connection__ ~= nil;
end

function Block:UpdateWidthHeightUnitCount()
    local maxWidthUnitCount, maxHeightUnitCount, widthUnitCount, heightUnitCount = 0, 0, 0, 0;                                                           -- 方块宽高
    for _, inputFieldContainer in ipairs(self.__input_field_container_list__) do
        local inputFieldContainerMaxWidthUnitCount, inputFieldContainerMaxHeightUnitCount, inputFieldContainerWidthUnitCount, inputFieldContainerHeightUnitCount = inputFieldContainer:UpdateWidthHeightUnitCount();
        inputFieldContainerWidthUnitCount, inputFieldContainerHeightUnitCount = inputFieldContainerWidthUnitCount or inputFieldContainerMaxWidthUnitCount, inputFieldContainerHeightUnitCount or inputFieldContainerMaxHeightUnitCount;
        
        widthUnitCount = math.max(widthUnitCount, inputFieldContainerWidthUnitCount);
        heightUnitCount = heightUnitCount + inputFieldContainerHeightUnitCount;
        maxWidthUnitCount = math.max(maxWidthUnitCount, inputFieldContainerMaxWidthUnitCount);
        maxHeightUnitCount = maxHeightUnitCount + inputFieldContainerMaxHeightUnitCount;
    end
    
    widthUnitCount = math.max(widthUnitCount, self:IsOutput() and 8 or 16);
    heightUnitCount = math.max(heightUnitCount, 6);
    maxWidthUnitCount = math.max(widthUnitCount, maxWidthUnitCount);
    maxHeightUnitCount = math.max(heightUnitCount, maxHeightUnitCount);

    if (self:IsStart()) then 
        heightUnitCount = heightUnitCount + self.CONNECTION_HEIGHT_UNIT_COUNT + self.START_CONNECTION_HEIGHT_UNIT_COUNT;
        maxHeightUnitCount = maxHeightUnitCount + self.CONNECTION_HEIGHT_UNIT_COUNT + self.START_CONNECTION_HEIGHT_UNIT_COUNT;
    elseif (self:IsStatement()) then
        heightUnitCount = heightUnitCount + self.CONNECTION_HEIGHT_UNIT_COUNT * 2;
        maxHeightUnitCount = maxHeightUnitCount + self.CONNECTION_HEIGHT_UNIT_COUNT * 2;
    else
        heightUnitCount = heightUnitCount + self.OUTPUT_CONNECTION_HEIGHT_UNIT_COUNT * 2;
        maxHeightUnitCount = maxHeightUnitCount + self.OUTPUT_CONNECTION_HEIGHT_UNIT_COUNT * 2;
        widthUnitCount = widthUnitCount + heightUnitCount;
        maxWidthUnitCount = maxWidthUnitCount + maxHeightUnitCount;
    end

    self:SetWidthHeightUnitCount(widthUnitCount, heightUnitCount);
    self:SetMaxWidthHeightUnitCount(maxWidthUnitCount, maxHeightUnitCount);
    
    local nextBlock = self:GetNextBlock();
    if (nextBlock) then 
        local _, _, _, _, nextBlockTotalWidthUnitCount, nextBlockTotalHeightUnitCount = nextBlock:UpdateWidthHeightUnitCount();
        self:SetTotalWidthHeightUnitCount(math.max(maxWidthUnitCount, nextBlockTotalWidthUnitCount), maxHeightUnitCount + nextBlockTotalHeightUnitCount);
    else
        self:SetTotalWidthHeightUnitCount(maxWidthUnitCount, maxHeightUnitCount);
    end

    local totalWidthUnitCount, totalHeightUnitCount = self:GetTotalWidthHeightUnitCount();
    return maxWidthUnitCount, maxHeightUnitCount, widthUnitCount, heightUnitCount, totalWidthUnitCount, totalHeightUnitCount;
end

-- 更新左上位置
function Block:UpdateLeftTopUnitCount()
    local leftUnitCount, topUnitCount = self:GetLeftTopUnitCount();
    local widthUnitCount, heightUnitCount = self:GetWidthHeightUnitCount();
    local offsetX, offsetY = leftUnitCount, topUnitCount;
   
    if (self:IsStart()) then 
        offsetY = topUnitCount + self.START_CONNECTION_HEIGHT_UNIT_COUNT;
    elseif (self:IsStatement()) then 
        offsetY = topUnitCount + self.CONNECTION_HEIGHT_UNIT_COUNT;
    else
        offsetY = topUnitCount + self.OUTPUT_CONNECTION_HEIGHT_UNIT_COUNT;
        offsetX = leftUnitCount + heightUnitCount / 2;
    end

    for i, inputFieldContainer in ipairs(self.__input_field_container_list__) do
        local _, inputFieldContainerHeightUnitCount = inputFieldContainer:GetWidthHeightUnitCount();
        inputFieldContainer:SetLeftTopUnitCount(offsetX, offsetY);
        inputFieldContainer:UpdateLeftTopUnitCount();
        offsetY = offsetY + inputFieldContainerHeightUnitCount;
    end
   
    local nextBlock = self:GetNextBlock();
    if (nextBlock) then
        local widthUnitCount, heightUnitCount = self:GetWidthHeightUnitCount();
        nextBlock:SetLeftTopUnitCount(leftUnitCount, topUnitCount + heightUnitCount);
        nextBlock:UpdateLeftTopUnitCount();
    end
end

function Block:OnSizeChange()
    local leftUnitCount, topUnitCount = self:GetLeftTopUnitCount();
    local widthUnitCount, heightUnitCount = self:GetWidthHeightUnitCount();
    if (self.__previous_connection__) then 
        self.__previous_connection__:SetLeftTopUnitCount(leftUnitCount, topUnitCount);
    end
    
    if (self.__next_connection__) then
        self.__next_connection__:SetLeftTopUnitCount(leftUnitCount, topUnitCount + heightUnitCount);
    end

    if (self.__output_connection__) then
        self.__output_connection__:SetLeftTopUnitCount(leftUnitCount, topUnitCount + heightUnitCount / 2);
    end
end

-- 更新图块布局
function Block:UpdateLayout()
    self:UpdateWidthHeightUnitCount();
    self:UpdateLeftTopUnitCount();
end

function Block:Render()
    local leftUnitCount, topUnitCount = self:GetLeftTopUnitCount();
    local widthUnitCount, heightUnitCount = self:GetWidthHeightUnitCount();
    self:SetBrushColor(self:GetColor());
    self:Translate(leftUnitCount, topUnitCount);

    if (self:IsOutput()) then
        self:DrawOutputBlock(widthUnitCount, heightUnitCount, nil, nil, true);
    else
        if (self:IsStart()) then
            self:DrawStartConnection(widthUnitCount, nil, nil, true);
        else
            self:DrawPrevConnection(widthUnitCount, nil, nil, true);
        end
        self:DrawNextConnection(widthUnitCount, 0, heightUnitCount - self.CONNECTION_HEIGHT_UNIT_COUNT, true);
    end

    self:Translate(-leftUnitCount, -topUnitCount);

    for _, inputFieldContainer in ipairs(self.__input_field_container_list__) do inputFieldContainer:Render() end

    local nextBlock = self:GetNextBlock();
    if (nextBlock) then nextBlock:Render() end
end


function Block:GetMouseUI(x, y)
    local left, top = self:GetLeftTop();
    local width, height = self:GetWidthHeight();
    local maxWidth, maxHeight = self:GetMaxWidthHeight();
    local totalWidth, totalHeight = self:GetTotalWidthHeight();

    -- 整个块区域内
    if (x < left or x > (left + totalWidth) or y < top or y > (top + totalHeight)) then return end

    -- 不在block内
    if (x < left or x > (left + maxWidth) or y < top or y > (top + maxHeight)) then 
        local nextBlock = self:GetNextBlock();
        return nextBlock and nextBlock:GetMouseUI(x, y, event);
    end

    -- 上下边缘高度
    local connection_height = (self:IsOutput() and self.OUTPUT_CONNECTION_HEIGHT_UNIT_COUNT or self.CONNECTION_HEIGHT_UNIT_COUNT) * self:GetUnitSize();
    local connection_width = self:IsOutput() and (height / 2) or 0;
    -- 在block上下边缘
    if (top < y and y < (top + connection_height)) then return self end
    if (y > (top + height - connection_height) and y < (top + height)) then return self end
    -- 左右边缘
    if (self:IsOutput() and (x < connection_width or x > (left + width - connection_width))) then return self end     
    -- 遍历输入
    for _, inputFieldContainer in ipairs(self.__input_field_container_list__) do
        local ui = inputFieldContainer:GetMouseUI(x, y);
        if (ui) then return ui end
    end

    return self:IsOutput() and self or nil;
end

function Block:OnMouseDown(event)
    self.__start_mouse_x__, self.__start_mouse_y__ = event:GetXY();
    self.__start_left_unit_count__, self.__start_top_unit_count__ = self:GetLeftTopUnitCount();
    self.__is_mouse_down__ = true;
end

function Block:OnMouseMove(event)
    if (not self.__is_mouse_down__ or not self:IsDraggable()) then return end
    local blockly = self:GetBlockly();
    local UnitSize = self:GetUnitSize();
    local x, y = event:GetXY();
    local XUnitCount = math.floor((x - self.__start_mouse_x__) / UnitSize);
    local YUnitCount = math.floor((y - self.__start_mouse_y__) / UnitSize);
    
    self:SetDragging(true);
    self:Disconnection(true);
    blockly:SetDraggingBlock(self);
    blockly:AddBlock(self);
    self:SetLeftTopUnitCount(self.__start_left_unit_count__ + XUnitCount, self.__start_top_unit_count__ + YUnitCount);
    self:UpdateLeftTopUnitCount();

    if (not blockly:IsInnerDeleteArea(blockly:ScreenXYToElementXY(x, y))) then
        blockly:GetShadowBlock():Shadow(self);
    end
end

function Block:OnMouseUp(event)
    self.__is_mouse_down__ = false;
    local blockly = self:GetBlockly();

    -- remove shadow block
    blockly:GetShadowBlock():Shadow(nil);
    blockly:SetDraggingBlock(nil);

    if (not self:IsDragging()) then return end 
    self:SetDragging(false);

    local x, y = blockly:ScreenXYToElementXY(event:GetXY());
    if (blockly:IsInnerDeleteArea(x, y)) then
        blockly:RemoveBlock(self);
    else
        if (self:TryConnectionBlock()) then
            -- 连接上
        else
            -- 事件让其处于顶层
            self:GetBlockly():AddBlock(self:GetTopBlock());
            -- 没有拖拽
            if (event:IsLeftButton()) then
                self:OnClick();
            else
                self:OnContextMenu();
            end
        end
    end
    blockly:OnWorkspaceChange();
end


function Block:TryConnectionBlock()
    self:GetBlockly():AddBlock(self);
    for _, block in ipairs(self:GetBlockly():GetBlockList()) do
        if (block ~= self and block:ConnectionBlock(self)) then return true end
    end
    return false;
end

function Block:IsIntersect(block, isSingleBlock)
    local leftUnitCount, topUnitCount = block:GetLeftTopUnitCount();
    local widthUnitCount, heightUnitCount = block:GetTotalWidthHeightUnitCount();
    local halfWidthUnitCount, halfHeightUnitCount = widthUnitCount / 2, heightUnitCount / 2;
    local centerX, centerY = leftUnitCount + halfWidthUnitCount, topUnitCount + halfHeightUnitCount;

    local blockLeftUnitCount, blockTopUnitCount = self:GetLeftTopUnitCount();
    local blockWidthUnitCount, blockHeightUnitCount = self:GetMaxWidthHeightUnitCount();
    if (not isSingleBlock) then blockWidthUnitCount, blockHeightUnitCount = self:GetTotalWidthHeightUnitCount() end
    local blockHalfWidthUnitCount, blockHalfHeightUnitCount = blockWidthUnitCount / 2, blockHeightUnitCount / 2;
    local blockCenterX, blockCenterY = blockLeftUnitCount + blockHalfWidthUnitCount, blockTopUnitCount + blockHalfHeightUnitCount;
    return math.abs(centerX - blockCenterX) <= (halfWidthUnitCount + blockHalfWidthUnitCount) and math.abs(centerY - blockCenterY) <= (halfHeightUnitCount + blockHalfHeightUnitCount + 12); -- 预留一个连接高度
end

function Block:ConnectionBlock(block)
    if (not block or self == block or block.__shadow_block__ == self or self.__shadow_block__ == block) then return end

    -- 实际匹配块
    local connection_block = block.__shadow_block__ or block;
    
    -- 只有顶层块才判定是否在整个区域内
    if ((not self.__previous_connection__ or not self.__previous_connection__:IsConnection()) and not self:IsIntersect(connection_block, false)) then return end

    -- 下个块如果相交则继续判定下个块
    local nextBlock = self:GetNextBlock();

    local isOutputBlock = block:IsOutput();
    -- 是否在块区域内
    if (self:IsIntersect(connection_block, true)) then
        -- 优先向下匹配
        if (nextBlock and not isOutputBlock and nextBlock.__next_connection__ and connection_block.__previous_connection__ and nextBlock.__next_connection__:IsMatch(connection_block.__previous_connection__)) then
            local blockLeftUnitCount, blockTopUnitCount = block:GetLeftTopUnitCount();
            local nextBlockLeftUnitCount, nextBlockTopUnitCount = nextBlock:GetLeftTopUnitCount();
            if ((nextBlockTopUnitCount + 4) < blockTopUnitCount and nextBlock:ConnectionBlock(block)) then return true end
        end

        -- 输入匹配
        for _, inputAndFieldContainer in ipairs(self.__input_field_container_list__) do 
            if (inputAndFieldContainer:ConnectionBlock(block)) then return true end
        end

        -- 下连接匹配
        if (not isOutputBlock and self.__next_connection__ and connection_block.__previous_connection__ and self.__next_connection__:IsMatch(connection_block.__previous_connection__)) then
            self:GetBlockly():RemoveBlock(block);
            local nextConnectionConnection = self.__next_connection__:Disconnection();
            self.__next_connection__:Connection(block.__previous_connection__);
            local lastNextBlock = block:GetLastNextBlock();
            if (lastNextBlock.__next_connection__) then 
                lastNextBlock.__next_connection__:Connection(nextConnectionConnection);
            else
                self:GetBlockly():AddBlock(nextConnectionConnection:GetBlock(), true);
            end
            block:SetLeftTopUnitCount(self.__left_unit_count__, self.__top_unit_count__ + self.__height_unit_count__);
            self:GetTopBlock():UpdateLayout();
            return true;
        end

        -- 上连接匹配
        local lastNextBlock = connection_block:GetLastNextBlock();  -- 获取最后一个连接
        if (not isOutputBlock and self.__previous_connection__ and lastNextBlock and lastNextBlock.__next_connection__ and self.__previous_connection__:IsMatch(lastNextBlock.__next_connection__)) then
            local previousConnectionConnection = self.__previous_connection__:Disconnection();
            self:GetBlockly():RemoveBlock(self);
            if (previousConnectionConnection) then previousConnectionConnection:Connection(block.__previous_connection__) end 
            self.__previous_connection__:Connection(block:GetLastNextBlock().__next_connection__);
            local totalWidthUnitCount, totalHeightUnitCount = connection_block:GetTotalWidthHeightUnitCount();
            if (block.__is_shadow_block__) then totalWidthUnitCount, totalHeightUnitCount = block:GetShadowWidthHeightUnitCount() end 
            block:SetLeftTopUnitCount(self.__left_unit_count__, self.__top_unit_count__ - totalHeightUnitCount);
            self:GetTopBlock():UpdateLayout();
            return true;
        end
    end

    return nextBlock and nextBlock:ConnectionBlock(block);
end

-- 断开链接 b_retain_next_connection 是否保留下连接
function Block:Disconnection(b_retain_next_connection)
    local connection = self.__output_connection__ and self.__output_connection__:Disconnection();
    local connection_block = connection and connection:GetBlock();
    if (connection_block) then connection_block:GetTopBlock():UpdateLayout() end  

    -- 是否保留下连接
    if (b_retain_next_connection) then
        connection = self.__previous_connection__ and self.__previous_connection__:Disconnection();
        connection_block = connection and connection:GetBlock();
        if (connection_block) then connection_block:GetTopBlock():UpdateLayout() end  
    else
        local previousConnection = self.__previous_connection__ and self.__previous_connection__:Disconnection();
        local nextConnection = self.__next_connection__ and self.__next_connection__:Disconnection();
    
        if (previousConnection) then
            previousConnection:Connection(nextConnection);
            previousConnection:GetBlock():GetTopBlock():UpdateLayout();
        else 
            if (nextConnection) then
                self:GetBlockly():AddBlock(nextConnection:GetBlock());
            end
        end
    end
end


function Block:SaveToXmlNode()
    local xmlnode = {name = "block", attr = {}};
    local attr = xmlnode.attr;

    attr.__left_unit_count__ = self.__left_unit_count__;
    attr.__top_unit_count__ = self.__top_unit_count__;
    attr.__type__ = self:GetType();

    for _, input_field_container in ipairs(self.__input_field_container_list__) do
        for _, input_field in ipairs(input_field_container:GetInputFieldList()) do
            local subxmlnode = input_field:SaveToXmlNode();
            if (subxmlnode) then table.insert(xmlnode, subxmlnode) end
        end
    end

    local nextblock = self:GetNextBlock();
    local subxmlnode = nextblock and nextblock:SaveToXmlNode();
    if (subxmlnode) then table.insert(xmlnode, subxmlnode) end 

    return xmlnode;
end

function Block:LoadFromXmlNode(xmlnode)
    if (xmlnode.name ~= "block") then return end 
    local attr = xmlnode.attr or {};

    self.__left_unit_count__ = tonumber(attr.__left_unit_count__) or 0;
    self.__top_unit_count__ = tonumber(attr.__top_unit_count__) or 0;

    local nextBlockXmlNode = nil;
    for _, subXmlNode in ipairs(xmlnode) do 
        if (subXmlNode.name == "field" or subXmlNode.name == "input") then
            local input_field = self:GetInputField(subXmlNode.attr and subXmlNode.attr.name);
            input_field:LoadFromXmlNode(subXmlNode);
        elseif (subXmlNode.name == "block") then
            local nextBlock = self:GetBlockly():NewBlockByXmlNode(subXmlNode);
            if (nextBlock) then
                self.__next_connection__:Connection(nextBlock.__previous_connection__);
            end
        end
    end
end


-- 获取字段值
function Block:GetInputFieldValue(input_field_name)
    local input_field = self:GetInputField(input_field_name);
    return input_field and input_field:GetInputFieldValue();
end

local _Byte = string.byte("_");
local aByte = string.byte("a");
local zByte = string.byte("z");
local AByte = string.byte("A");
local ZByte = string.byte("Z");
local _0Byte = string.byte("0");
local _9Byte = string.byte("9");
local function ToVarFuncName(str)
    local newstr = "";
    for i = 1, #str do
        local byte = string.byte(str, i, i);
        if (_Byte == byte or (aByte <= byte and byte <=zByte) or (AByte <= byte and byte <=ZByte) or (_0Byte <= byte and byte <= _9Byte)) then
            newstr = newstr .. string.char(byte);
        else 
            newstr = newstr .. string.format("_%X", byte)
        end
    end
    return newstr;
end

function Block:GenerateCode()
    local toCode = self:GetOption().toCode;

    if (type(toCode) == "function") then
        return toCode(self);
    end

    if (type(toCode) ~= "string") then
        return "";
    end

    -- code description
    local args = {};
    for name, input_field in pairs(self.__input_field_map__) do
        args[name] = input_field:GetInputFieldValue();
    end

    local NEXT_BLOCK_CODE = nil;
    local code = toCode or "";
    code = string.gsub(code, "^%s*", "")
    code = string.gsub(code, "[;%s]*$", "")
    code = string.gsub(code, "%$%{([%w%_]+)%}", args);      -- ${name} 取字段值
    -- ${NEXT_BLOCK_CODE}
    code = string.gsub(code, "%$%{NEXT%_BLOCK%_CODE%}", function()
        if (NEXT_BLOCK_CODE) then return NEXT_BLOCK_CODE end 
        local nextBlock = self:GetNextBlock();
        NEXT_BLOCK_CODE = nextBlock and nextBlock:GetAllCode();
        return NEXT_BLOCK_CODE;
    end);     
    -- $VAR_FUNC_NAME(name)
    code = string.gsub(code, "%$VAR%_FUNC%_NAME%(([%w%_]+)%)", function(name)
        return ToVarFuncName(args[name]);
    end)
    -- statement append return
    if (not self:IsOutput()) then code = code .. "\n" end
    return code;
end

function Block:GetCode()
    return self:GenerateCode();
end

function Block:GetAllCode()
    local allcode = self:GetCode();
    local nextblock = self:GetNextBlock();
    if (nextblock) then allcode = allcode .. nextblock:GetAllCode() end
    return allcode;
end

return Block;